/*
 *   Copyright (C) 2010  Jens Kieselbach
 * 
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 * 
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 * 
 *   You may obtain a copy of the License at http://www.gnu.org/licenses/gpl.html
 */

package de.kieselbach.grootional.component

import de.kieselbach.swing.*
import de.kieselbach.swing.border.*

import com.jgoodies.looks.*
import com.jgoodies.looks.plastic.*
import groovy.swing.*
import javax.swing.*
import javax.swing.border.*

import java.awt.BorderLayout
import java.awt.Color
import java.awt.Dimension
import java.awt.Font
import java.awt.FlowLayout
import java.awt.Point

class TabbedCardPane  extends JPanel {

    final static int TAB_STEP = 40
    final static int EXPANDED_SPACE = 3
    final static int STEP_BUTTON_WIDTH = 25

    final def borderColor
	final def activeBorder
	final def inactiveBorder
    final def spaceBorder
    final def leftBorderNoLine
    final def leftBorderLine
    final def rightBorderNoLine
    final def rightBorderLine

    final def parent
    final def uiResources
    final def resourceBundle
    final def editable

    def buttonScrollPane
    def buttonPanel
    def containerPanel

    def cards = [] as List
    def visibleCardIndex

    def visibleInternalCard
    def lastInternalCard

    def count = 0

    TabbedCardPane(def par, def uires) {

        this(par, uires, true, 5, 5, 5, 5)
    }

    TabbedCardPane(def par, def uires, def edit) {
        this(par, uires, edit, 5, 5, 5, 5)
    }

    TabbedCardPane(def par, def uires, def edit, def bt, def bl, def bb, def br) {

        super(new BorderLayout(0, 0))

        parent = par
        uiResources = uires
        resourceBundle = uiResources.resourceBundles['gui']
        editable = edit

        borderColor = UIUtilities.lineBorderColor
		activeBorder = CustomLineBorder.create(borderColor, '47896', 3, 5, 3, 5)
		inactiveBorder = CustomLineBorder.create(borderColor, '47896321', 3, 5, 3, 5)
        spaceBorder = BorderFactory.createEmptyBorder(0, 0, EXPANDED_SPACE, 0)
        leftBorderNoLine = BorderFactory.createEmptyBorder(0, 0, EXPANDED_SPACE, EXPANDED_SPACE + 1)
        leftBorderLine = BorderFactory.createCompoundBorder(CustomLineBorder.create(borderColor, '6', 0, 2 ,0 ,0), BorderFactory.createEmptyBorder(0, 0, EXPANDED_SPACE, EXPANDED_SPACE))
        rightBorderNoLine = BorderFactory.createEmptyBorder(0, EXPANDED_SPACE + 1, EXPANDED_SPACE, 0)
        rightBorderLine = BorderFactory.createCompoundBorder(CustomLineBorder.create(borderColor, '4', 0, 0, 0, 2), BorderFactory.createEmptyBorder(0, EXPANDED_SPACE, EXPANDED_SPACE, 0))

        def contentPanel = new JPanel(new BorderLayout(0, 0))
        contentPanel.border = BorderFactory.createEmptyBorder(bt, bl, bb, br)
        add(contentPanel)

        buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0))
        containerPanel = new JPanel(new BorderLayout(0, 0))

        buttonScrollPane = new JScrollPane(buttonPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER)
        buttonScrollPane.border = null

        def top = new JPanel(new BorderLayout(0, 0))
        top.add(buttonScrollPane, BorderLayout.CENTER)

        def leftButton = new JButton(uiResources.images['stepleft'])
        leftButton.verticalAlignment = PaneBuilder.verticalButtonAligment
        def left = new JPanel(new BorderLayout(0, 0))
        left.add(leftButton, BorderLayout.CENTER)
        left.border = leftBorderNoLine
        top.add(left, BorderLayout.WEST)
        left.preferredSize = [STEP_BUTTON_WIDTH, left.preferredSize.@height] as Dimension

        def rightButton = new JButton(uiResources.images['stepright'])
        rightButton.verticalAlignment = PaneBuilder.verticalButtonAligment
        def right = new JPanel(new BorderLayout(0, 0))
        right.add(rightButton, BorderLayout.CENTER)
        right.border = rightBorderNoLine
        top.add(right, BorderLayout.EAST)
        right.preferredSize = [STEP_BUTTON_WIDTH, right.preferredSize.@height] as Dimension

        contentPanel.add(top, BorderLayout.NORTH)
        contentPanel.add(containerPanel, BorderLayout.CENTER)

        // -------------------------------------------------------------------

        buttonScrollPane.viewport.stateChanged = {
            int pw = buttonPanel.preferredSize.@width
            int w = buttonScrollPane.width
            if(pw > w) {
                int nx = buttonScrollPane.viewport.viewPosition.@x
                int mw = buttonPanel.preferredSize.@width - buttonScrollPane.width
                left.border = nx > 0 ? leftBorderLine : leftBorderNoLine
                right.border = nx < mw ? rightBorderLine : rightBorderNoLine

                leftButton.visible = true
                rightButton.visible = true
            }
            else {
                left.border = leftBorderNoLine
                right.border = rightBorderNoLine

                leftButton.visible = false
                rightButton.visible = false
            }
        }

        leftButton.actionPerformed = {
            int nx = buttonScrollPane.viewport.viewPosition.@x
            if(nx > 0) {
                nx -= TAB_STEP
                if(nx < 0) {
                    nx = 0
                }
                buttonScrollPane.viewport.viewPosition = [nx, 0] as Point
            }
        }

        rightButton.actionPerformed = {
            int nx = buttonScrollPane.viewport.viewPosition.@x
            int mw = buttonPanel.preferredSize.@width - buttonScrollPane.width
            if(nx < mw) {
                nx += TAB_STEP
                if(nx > mw) {
                    nx = mw
                }
                buttonScrollPane.viewport.viewPosition = [nx, 0] as Point
            }
        }
    }

    void addCard(def card) {

        addCard(card, false)
    }

    void addCard(def card, def expanded) {

        if(card == null) {
            return
        }

        if(!expanded) {
            expanded = count <= 0
        }

        if(expanded && visibleInternalCard != null) {
            containerPanel.remove(visibleInternalCard.box)
            visibleInternalCard.button.preferredSize = [visibleInternalCard.button.preferredSize.@width, visibleInternalCard.button.preferredSize.@height - EXPANDED_SPACE] as Dimension
			visibleInternalCard.button.border = inactiveBorder
            visibleInternalCard.button.font = visibleInternalCard.button.font.deriveFont(Font.PLAIN)
            visibleInternalCard.buttonPanel.border = spaceBorder

            visibleInternalCard = null
            visibleCardIndex = null
        }
      
        cards.each() {p->
            if(p == card) {
                throw new IllegalArgumentException('the panel was already added!')
            }
        }

        def title = 'undefined'
        try {
            title = card.title
        }
        catch(Exception exc) {
            exc.printStackTrace()
        }

        def sb = new ExtendedSwingBuilder()
        def button = sb.processableButton(title, icon: card.icon, requestFocusEnabled: false, verticalAlignment: SwingConstants.CENTER,
            focusPainted: false, horizontalTextPosition: SwingConstants.RIGHT, horizontalAlignment: SwingConstants.LEFT)

        if(card.processable) {
            button.setProcessingImage(uiResources.images['process-working'], 4, 8)
            card.processableButton = button

            if(card.processing) {
                button.start()
            }
        }

        if(count > 0) {
            buttonPanel.add(Box.createHorizontalStrut(4))
        }

        def bp = new JPanel(new BorderLayout(0, 0))
        bp.add(button, BorderLayout.CENTER)
        if(!expanded) {
            bp.border = spaceBorder
        }

        buttonPanel.add(bp)

        def bar = null
        def box = sb.panel() {

            sb.borderLayout()

            sb.panel(constraints: BorderLayout.CENTER) {
                sb.borderLayout()

                sb.panel(border: CustomLineBorder.create(borderColor, '486', 2, 2, 1, 2), constraints: BorderLayout.NORTH) {
                    sb.borderLayout()

                    bar = sb.box(constraints: BorderLayout.CENTER)
                }

                widget(card)
            }
        }

        def internalcard = new InternalCard(card, button, box, cards.size(), bp)
        cards << internalcard.card

        if(expanded) {

            visibleInternalCard = internalcard
            visibleCardIndex = internalcard.index

            button.preferredSize = [button.preferredSize.@width + 20, button.preferredSize.@height + EXPANDED_SPACE] as Dimension
			button.border = activeBorder
            button.font = button.font.deriveFont(Font.BOLD)

            containerPanel.add(box, BorderLayout.CENTER)
        }
        else {
            button.preferredSize = [button.preferredSize.@width + 20, button.preferredSize.@height] as Dimension
        }

        button.actionPerformed = {
            if (visibleCardIndex != internalcard.index) {
                switchCard(internalcard)
            }
        }

        // ---------------------------------------------------------------------

        def maxh = 0
        def separators = [] as List

        card.toolBarItems.each {elem ->

            if(elem instanceof AbstractButton) {
                elem.verticalAlignment = PaneBuilder.verticalButtonAligment
            }
            else if(elem instanceof CardSeparator) {
                separators << elem
            }

            bar.add(elem)

            def h = elem.preferredSize.@height
            if(h > maxh) {
                maxh = h
            }
        }

        if(editable) {
            if(card.addToolBarGlue) {
                bar.add(Box.createHorizontalGlue())
            }

            def up = new FlatButton(uiResources.images['left16'] as Icon)
            up.verticalAlignment = PaneBuilder.verticalButtonAligment
            internalcard.upButton = up
            up.toolTipText = resourceBundle.getString('card.moveleft')
            up.enabled = lastInternalCard != null
            bar.add(up)
            up.actionPerformed = {
                moveCard(internalcard, true)
            }

            def down = new FlatButton(uiResources.images['right16'] as Icon)
            down.verticalAlignment = PaneBuilder.verticalButtonAligment
            internalcard.downButton = down
            down.toolTipText = resourceBundle.getString('card.moveright')
            down.enabled = false
            bar.add(down)
            down.actionPerformed = {
                moveCard(internalcard, false)
            }

            def sep = new CardSeparator()
            separators << sep
            bar.add(sep)

            def close = new FlatButton(uiResources.images['close16'] as Icon)
            close.verticalAlignment = PaneBuilder.verticalButtonAligment
            close.toolTipText = resourceBundle.getString('card.close')
            bar.add(close)
            close.actionPerformed = {
                closeCard(internalcard)
            }

            card.closeButton = close
            card.closeIcon = uiResources.images['close16']
            card.closeModifiedIcon = uiResources.images['closemodified16']

            if(card.processable) {
                if(card.processing) {
                    card.closeButton.enabled = false
                    card.closeButtonState = true
                }
            }

            def h = close.preferredSize.@height
            if(h > maxh) {
                maxh = h
            }

            def size = [7, maxh] as Dimension
            separators.each() {sepelem ->
                sepelem.minimumSize = sepelem.maximumSize = sepelem.preferredSize = size
            }

            if(lastInternalCard != null) {
                lastInternalCard.downButton.enabled = true
            }
        }

        // ------------------------------------------------------------

        lastInternalCard = internalcard
        count++

        buttonScrollPane.validate()
        buttonScrollPane.repaint()

        containerPanel.validate()
        containerPanel.repaint()

        if(expanded) {
            int pw = buttonPanel.preferredSize.@width
            int w = buttonScrollPane.width
            if(pw > w) {
                buttonScrollPane.viewport.viewPosition = [pw - w, 0] as Point
            }
        }
    }

    // --------------------------------------------------------------------------------------------------

    void switchCard(def internalcard) {

        visibleInternalCard.button.preferredSize = [visibleInternalCard.button.preferredSize.@width, visibleInternalCard.button.preferredSize.@height - EXPANDED_SPACE] as Dimension
		visibleInternalCard.button.border = inactiveBorder
        visibleInternalCard.button.font = visibleInternalCard.button.font.deriveFont(Font.PLAIN)
        visibleInternalCard.buttonPanel.border = spaceBorder

        containerPanel.remove(visibleInternalCard.box)

        internalcard.button.preferredSize = [internalcard.button.preferredSize.@width, internalcard.button.preferredSize.@height + EXPANDED_SPACE] as Dimension
		internalcard.button.border = activeBorder
        internalcard.button.font = internalcard.button.font.deriveFont(Font.BOLD)
        internalcard.buttonPanel.border = null

        containerPanel.add(internalcard.box, BorderLayout.CENTER)

        visibleInternalCard = internalcard
        visibleCardIndex = internalcard.index

        buttonScrollPane.validate()
        buttonScrollPane.repaint()

        containerPanel.validate()
        containerPanel.repaint()
    }

    void moveCard(def internalcard, def up) {

        def oldcards = cards
        def oi = oldcards.indexOf(internalcard.card)
        if(oi >= 0) {

            def oldcard = oldcards.remove(oi)
            if(up) {
                oldcards.add(oi - 1, oldcard)
            }
            else {
                oldcards.add(oi + 1, oldcard)
            }

            cards = [] as List

            buttonPanel.removeAll()
            containerPanel.removeAll()
            visibleInternalCard = null
            visibleCardIndex = null
            lastInternalCard = null
            count = 0

            oldcards.each() {card ->
                addCard(card, card == oldcard)
            }

            buttonScrollPane.validate()
            buttonScrollPane.repaint()

            containerPanel.validate()
            containerPanel.repaint()
        }
    }

    void closeCard(def internalcard) {

        def panebuilder = new PaneBuilder()
        if(panebuilder.question(resourceBundle.getString('card.close'), message: resourceBundle.getString('card.closeconfirmation'), owner: parent, options: ['yes', 'no'], default: 'yes') == 'yes') {

            internalcard.card.dispose()

            def oldcards = cards
            oldcards.remove(internalcard.card)
            cards = [] as List

            buttonPanel.removeAll()
            containerPanel.removeAll()
            visibleInternalCard = null
            visibleCardIndex = null
            lastInternalCard = null
            count = 0

            oldcards.each() {card ->
                addCard(card)
            }

            buttonScrollPane.validate()
            buttonScrollPane.repaint()

            containerPanel.validate()
            containerPanel.repaint()
        }
    }
}
